using System;
using System.Collections.Generic;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Audio;

using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Collisions;

#if WIIMOTE
using WiimoteLib;
#endif

using StupidSpaceThing.MenuSystem;
using StupidSpaceThing;

namespace RacerGame
{
	/// <summary>
	/// This is the main type for your game
	/// </summary>
	public class RacerGame : StupidSpaceThing.BaseGame
	{
		public static bool ShowCollisionObjects = false;
		public static bool GenCollisionObjects = true;
		GraphicsDeviceManager graphics;
		GraphicsDevice device;
		SpriteBatch spriteBatch;
		public static SpriteFont SpriteFont;
		public static Random Random = new Random();
		public static bool Ended = false;
		ContentManager Content;

		int frameRate = 0;
		int frameCounter = 0;
		TimeSpan elapsedTime = TimeSpan.Zero;

		Camera camera;
		float cameraZoom = 1;
		internal static Camera CurCamera;
		//Vector3 defaultCamPos = new Vector3(0, 35, -55);
		Vector3 defaultCamPos = new Vector3(0, 25, -50);
		float camRotate = 0.4f;

		internal static Map CurMap;
		internal static Map NextMap;
		internal static SubSurface CurSubSurface;
		internal static SubSurface NextSubSurface;
		Texture2D heightMap;
		internal static Dictionary<PlayerIndex, Player> Players = new Dictionary<PlayerIndex, Player>();
		Vector3 lastPlayerPos;
		bool resetting = true;
		int padsConnected = 0;

		public static Texture2D SelectionTexture;
		public static Texture2D RectangleTexture;
        
		BasicEffect basic; 

		bool camRotateWithBullets = true;
		float lastCamRotate = 0;
		float totalCamMove = 0;

		bool showScore = false;

		public static PhysicsSimulator Simulator;

		AudioEngine audioEngine;
		public static SoundBank SoundBank;
		WaveBank waveBank;

		public static AudioListener Listener = new AudioListener();
		public static bool PlaySound = false;

		Cue motor;
		Cue shot;
		Cue explode;

		int level = 1;

		public RacerGame(ContentManager content, GraphicsDeviceManager graphics) {
			this.Content = content;
			this.graphics = graphics;			
			// full screen
			//graphics.PreferredBackBufferWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
			//graphics.PreferredBackBufferHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
			/*graphics.IsFullScreen = true;*/
			//graphics.PreferredBackBufferWidth = 1000;
			//graphics.PreferredBackBufferHeight = 600;
		}

		public override void Initialize() {
			device = graphics.GraphicsDevice;
            TextureTools.Init(device);
            ShaderSystem.Init(device, Content.Load<Effect>(@"Effects\NeonShader"));
			device.RenderState.SourceBlend = Blend.SourceAlpha;
			device.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
			device.RenderState.AlphaBlendEnable = true;
			LoadContent();
			Quad.InitGraphics(device);
			Simulator = new PhysicsSimulator(new Vector2(0, 0));
			Simulator.MaxContactsToDetect = 2;			
			//map = new Map(device, heightMap, 2);
			CurMap = new Map(device, 20, 70, 3);
            CurMap.LoadEffect(Content.Load<Effect>(@"RacerEffects\QuadWireframe"));
			NextMap = new Map(device, 20, 70, 3);
			NextMap.LoadEffect(Content.Load<Effect>(@"RacerEffects\QuadWireframe"));
			CurSubSurface = new SubSurface(CurMap);
			NextSubSurface = new SubSurface(NextMap);
			NextSubSurface.Height = -4.9f;
			NextSubSurface.UpdatePosition(NextMap);
			Bullets.Init(device);
			StupidSpaceThing.TextureTools.Init(device);
            //Background.Init(device);
            SubSurface.Init(device);
			initEffects();
			RealInput.Listen(Buttons.X, commands);
			RealInput.Listen(Buttons.B, commands);
			RealInput.Listen(Buttons.Y, commands);
			RealInput.Listen(Buttons.A, commands);
			RealInput.Listen(Buttons.LeftTrigger, commands);


			while( !reset() ) ;
		}

		private bool reset() {
			showScore = true;
			resetting = true;
			Ended = true;
			bool ready = true;
			foreach( Faction faction in Faction.Factions ) {
				foreach( FactionEntity fe in faction.FactionEntities ) {
					if( !fe.Dead ) {
						ready = false;
						fe.Entity.Die();
					}					
				}		
			}
			if( !ready ) { return ready; }
			showScore = false;
			resetting = false;
			// cleanup
			Simulator.Clear();
			Faction.Clear();
			CurMap.Clear();
			CurMap.ZOffset = 0;
			CurSubSurface.UpdatePosition(CurMap);
			NextMap.Clear();
			NextMap.ZOffset = CurMap.Height * CurMap.CellSize;
			NextSubSurface.UpdatePosition(NextMap);
			level = 0;
			// neutral			
			//Faction.None.Add(new TradePortal(CurMap.Center, (float)(Math.PI / 2), 100, 1));
			

			//Faction.None.Add(new JellyFish(CurMap.Center + new Vector3(0, 0, -50)));
			padsConnected = 0;
			Dictionary<PlayerIndex, Player> newPlayers = new Dictionary<PlayerIndex, Player>();
			foreach( PlayerIndex index in RealInput.GamePadIndices ) {
				if( GamePad.GetState(index).IsConnected ) {
					padsConnected++;
				}
			}
			if( padsConnected == 0 ) {			
				return false;
			}

			// factions
			new Blue(CurMap.TopLeft + new Vector3(-80, 0, -20));
			new Red(CurMap.TopRight + new Vector3(80, 0, -20));
			new Yellow(CurMap.BottomRight + new Vector3(80, 0, 20));
			new Green(CurMap.BottomLeft + new Vector3(-80, 0, 20));

			float spacing = 10 * (padsConnected - 1);
			float offset = 10 * (padsConnected - 1) / 2.0f;
			Faction[] addToFactions = { Faction.Green, Faction.Red, Faction.Blue, Faction.Yellow };
			foreach( PlayerIndex index in RealInput.GamePadIndices ) {
				if( GamePad.GetState(index).IsConnected ) {
					Player player = new Player(new Vector3(CurMap.Width * CurMap.CellSize / 2 + offset - spacing * newPlayers.Count, Map.MaxHeight + 0.2f, 2));
					((PlayerEntity)player.Entity).Init(device);
					newPlayers[index] = player;
					((PlayerEntity)player.Entity).PlayerIndex = index;
					addToFactions[newPlayers.Count - 1].Add(newPlayers[index]);
				}
			} 
			foreach( KeyValuePair<PlayerIndex, Player> pair in RacerGame.Players ) {
				if( newPlayers.ContainsKey(pair.Key) ) {
					newPlayers[pair.Key].Score = pair.Value.Score;
				}
			}
			Players = newPlayers;

			lastPlayerPos = playerAvgPosition();
			camera = new Camera(device, defaultCamPos * cameraZoom + lastPlayerPos, 0, 0);
			CurCamera = camera;
			lastCamRotate = 0;
			totalCamMove = 0;
			camera.Rotate(0, camRotate);
			Ended = false;
			return true;
		}

		private void initEffects() {
			basic = new BasicEffect(device, null);
			basic.FogColor = Color.Black.ToVector3();
			basic.FogEnabled = true;
			basic.FogStart = 10.0f;
			basic.FogEnd = 90.0f;			
			basic.EnableDefaultLighting();
		}

		public override void LoadContent() {
			spriteBatch = new SpriteBatch(device);
			SpriteFont = Content.Load<SpriteFont>(@"Fonts\FpsFont");
			SelectionTexture = Content.Load<Texture2D>(@"Textures\Selection");
			RectangleTexture = Content.Load<Texture2D>(@"Textures\Rectangle");
			PlayerEntity.InitModel(Content.Load<Model>(@"Models\Player"));
			Pylon.InitModel(Content.Load<Model>(@"Models\Pylon"));
			Pylon2.InitModel(Content.Load<Model>(@"Models\Pylon2"));
			SunstarEntity.InitModel(Content.Load<Model>(@"Models\Sunstar"));
			HiveEntity.InitModel(Content.Load<Model>(@"Models\Hive"));
			TurretEntity.InitModel(Content.Load<Model>(@"Models\Turret"));
			TradePortalEntity.InitModel(Content.Load<Model>(@"Models\TradePortal"));
			DataTrainEntity.InitModel(Content.Load<Model>(@"Models\DataTrain\Full"), Content.Load<Model>(@"Models\DataTrain\Empty"));
			MineEntity.InitModel(Content.Load<Model>(@"Models\Mine"));
			ScorpionEntity.InitModel(Content.Load<Model>(@"Models\Scorpion"));
			JellyFishEntity.InitModel(Content.Load<Model>(@"Models\JellyFish"));
			heightMap = Content.Load<Texture2D>(@"Maps\Map");
			Bullets.Init(Content.Load<Effect>(@"RacerEffects\PointSprite"), Content.Load<Texture2D>(@"Textures\SpriteTest"));
            //Background.LoadTextures(Content.Load<Texture2D>(@"Textures\skytest_background"), Content.Load<Texture2D>(@"Textures\skytest_foreground"));
            SubSurface._Background = Content.Load<Texture2D>(@"Textures\ss_background");
			SubSurface._Foreground = Content.Load<Texture2D>(@"Textures\ss_foreground");
            SubSurface._Effect = Content.Load<Effect>(@"RacerEffects\SubSurface");

			audioEngine = new AudioEngine(@"Content\Sound\Beta.xgs");

			SoundBank = new SoundBank(audioEngine, @"Content\Sound\Sound Bank.xsb");
			waveBank = new WaveBank(audioEngine, @"Content\Sound\Wave Bank.xwb");

			Listener.Forward = Vector3.Backward;

			motor = SoundBank.GetCue("motor_1");
			shot = SoundBank.GetCue("shot");
			explode = SoundBank.GetCue("explode");
		}

		public override void UnloadContent() {
			Faction.Clear();
			CurMap.Stop();
			NextMap.Stop();
			Simulator.Clear();
		}

		private void commands(Buttons button, Action action, PlayerIndex index, RealGameTime gameTime) {
			if( action == Action.Click ) {
				switch( button ) {
					/*case Buttons.Y:
						foreach( Faction faction in Faction.Factions ) {
							foreach( FactionEntity fe in faction.FactionEntities ) {
								if( fe is Mine ) {
									fe.Entity.Die();
								}
							}
						}
						for( int i = 0; i < 20; i++ ) {
							Faction.RandomFaction().Add(new Mine(new Vector3(Game1.Random.Next(0, (int)(CurMap.Width * CurMap.CellSize)), 0, Game1.Random.Next(0, (int)(CurMap.Height * CurMap.CellSize)))));
						}
						break;
					case Buttons.X:
						rotateWithBullets = !rotateWithBullets;
						break;
					case Buttons.B:
						if( GenCollisionObjects ) {
							ShowCollisionObjects = !ShowCollisionObjects;
						}
						break;
					case Buttons.A:
						player.Entity.Die();
						break;*/
					case Buttons.Y:
						Players[index].ChangeFaction(Faction.Yellow, gameTime);
						break;
					case Buttons.X:
						Players[index].ChangeFaction(Faction.Blue, gameTime);
						break;
					case Buttons.B:
						Players[index].ChangeFaction(Faction.Red, gameTime);
						break;
					case Buttons.A:
						Players[index].ChangeFaction(Faction.Green, gameTime);
						break;					
					case Buttons.LeftTrigger:
						CurMap.Bomb(Players[index].Position2D, 1f);
						break;
				}
			}
		}

		private Vector3 playerAvgPosition() {
			Vector3 avgPosition = Vector3.Zero;
			int alivePlayers = 0;
			foreach( KeyValuePair<PlayerIndex, Player> pair in Players ) {
				if( !pair.Value.Dead ) {
					avgPosition += pair.Value.Position;
					alivePlayers++;
				}
			}
			avgPosition /= alivePlayers;
			return avgPosition;
		}

		public static void PlayCue(string cue, AudioListener listener, AudioEmitter emitter) {
			if( PlaySound ) {
				SoundBank.PlayCue(cue, listener, emitter);
			}
		}

		internal override void Update(RealGameTime gameTime) {			
			// Allows the game to exit

			if( resetting && !reset() ) {
				// update entities
				foreach( Faction faction in Faction.Factions ) {
					foreach( FactionEntity fe in faction.FactionEntities ) {
						fe.Update(gameTime);
					}
					foreach( DataTrain dt in faction.DataTrains ) {
						dt.Update(gameTime);
					}
				}
				return;
			}

			Vector2 force;

			int alive = 0;
			foreach( KeyValuePair<PlayerIndex, Player> pair in Players ){
				if( pair.Value.Position.Z < camera.Position.Z + 24 ) {
					pair.Value.Entity.Die();
				}
				if( !pair.Value.Entity.Dead ) {
					alive++;
				}

				GamePadState gState = GamePad.GetState(pair.Key);
				// gamepad			 
				force = 150 * gState.ThumbSticks.Left;
#if WIIMOTE
				if( TopMenu.WiiState != null ) {
					if( pair.Key == PlayerIndex.One ) {
						WiimoteState state = TopMenu.WiiState;
						force.X = state.AccelState.X * 150;
						force.Y = state.AccelState.Y * 150;
					}
				}
#endif
				force.X = -force.X;
				pair.Value.ApplyChainedForce(force, true);
								
				if( gState.ThumbSticks.Right.X != 0 || gState.ThumbSticks.Right.Y != 0 ) {
					Bullets.Fire(Color.White, pair.Value, new Vector3(-gState.ThumbSticks.Right.X, 0, gState.ThumbSticks.Right.Y), 0, gameTime);
					//SoundBank.PlayCue("shot_enemy");
				}
				if( pair.Key == PlayerIndex.One ) {
#if WIIMOTE
					if( TopMenu.Nunchuk && (Math.Abs(TopMenu.WiiState.NunchukState.X) > 0.1 || Math.Abs(TopMenu.WiiState.NunchukState.Y) > 0.1) ) {
						Bullets.Fire(Color.White, pair.Value, new Vector3(-TopMenu.WiiState.NunchukState.X, 0, TopMenu.WiiState.NunchukState.Y), 0, gameTime);
					}
#endif
				}

				pair.Value.ApplyForce(new Vector2(0, 1) * 300, true);
			}

			if( (alive < 2 && padsConnected > 1 ) || alive < 1 ) {
				resetting = true;
			}

			/*GamePadState gState = GamePad.GetState(PlayerIndex.One);
			// gamepad			 
			force = 80 * gState.ThumbSticks.Left;
			force.X = -force.X;
			player1.ApplyForce(force, true);
			player1.ApplyForce(new Vector2(0, 1) * (100 + level * 10), true);
			force = 80 * gState.ThumbSticks.Right;
			force.X = -force.X;
			player2.ApplyForce(force, true);
			player2.ApplyForce(new Vector2(0, 1) * (100 + level * 10), true);*/

			RealInput.Update(gameTime);
						
			KeyboardState kState = Keyboard.GetState();
			// keyboard
			/*force = new Vector2((kState.IsKeyDown(Keys.A) ? 1 : 0) + (kState.IsKeyDown(Keys.D) ? -1 : 0), (kState.IsKeyDown(Keys.W) ? 1 : 0) + (kState.IsKeyDown(Keys.S) ? -1 : 0));
			force *= 80;
			player1.ApplyForce(force, true);
			Vector3 kFireDirection = Vector3.Zero;
			if( kState.IsKeyDown(Keys.Right) ) kFireDirection -= Vector3.Right;
			if( kState.IsKeyDown(Keys.Left) ) kFireDirection -= Vector3.Left;
			if( kState.IsKeyDown(Keys.Up) ) kFireDirection += Vector3.Backward;
			if( kState.IsKeyDown(Keys.Down) ) kFireDirection += Vector3.Forward;
			if( kFireDirection != Vector3.Zero ) {
				Bullets.Fire(Color.White, player1, kFireDirection, 0, gameTime);
			}*/

						
			Simulator.Update(gameTime.ElapsedGameTime.Milliseconds * 0.001f);

			PathMover.Update(gameTime);

			// rotate camera with bullets
			/*float camRotate = 0;
			if( camRotateWithBullets && (gState.ThumbSticks.Right.X != 0 || gState.ThumbSticks.Right.Y != 0) ) {
				if( gState.ThumbSticks.Right.Y >= 0 ) {
					camRotate = (float)(Math.Atan2(gState.ThumbSticks.Right.Y, gState.ThumbSticks.Right.X) - Math.PI / 2);
				} else {
					camRotate = -(float)(Math.PI / 2 + Math.Atan2(gState.ThumbSticks.Right.Y, gState.ThumbSticks.Right.X));
				}
			}
			camRotate = MathHelper.Clamp(camRotate, lastCamRotate - 0.01f, lastCamRotate + 0.01f);
			camRotate = MathHelper.Clamp(camRotate, -0.3f, 0.3f);
			camera.RotateSmooth(camRotate - lastCamRotate, 0);
			lastCamRotate = camRotate;*/
			// move camera when shooting backwards
			float camMove = 0;
			/*if( camRotateWithBullets ) {
				if( gState.ThumbSticks.Right.Y < 0 ) {
					if( totalCamMove > -30 ) {
						camMove = -0.5f;
					}
				} else if( totalCamMove < 0 ) {
					camMove = 0.5f;
				}
			}
			totalCamMove += camMove;*/

			if( camera.Position.Z > NextMap.ZOffset + 10 ) {
				// move map
				Map tempMap = CurMap;
				CurMap = NextMap;
				NextMap = tempMap;
				NextMap.ZOffset = CurMap.ZOffset + CurMap.Height * CurMap.CellSize;
				// move subsurface
				SubSurface tempSubSurface = CurSubSurface;
				CurSubSurface = NextSubSurface;
				NextSubSurface = tempSubSurface;
				NextSubSurface.UpdatePosition(NextMap);
				// add mines
				for( int i = 0; i < level; i++ ) {
					Faction mineFaction = Faction.RandomFaction();
					Map.Cell freeCell = NextMap.GetFreeCell();
					mineFaction.Add(new Mine(new Vector3(freeCell.X * NextMap.CellSize, 0, freeCell.Z * NextMap.CellSize + NextMap.ZOffset)));
				}
				level++;
			}


			audioEngine.Update();

			/*if( !motor.IsPlaying ) {
				if( motor.IsStopped || motor.IsStopping ) {
					//motor = SoundBank.GetCue("motor_1");
				}
				//motor.Play();
			}*/
			//motor.SetVariable("DopplerPitchScalar", (float)Math.Pow(player.Entity.ColObject.LinearVelocity.Length(),0.3));

			/*camera.Move(new Vector3(-gState.ThumbSticks.Left.X / 10.0f, 0, gState.ThumbSticks.Left.Y / 10.0f));*/
			//camera.RotateSmooth(-gState.ThumbSticks.Right.X / 100.0f, -gState.ThumbSticks.Right.Y / 100.0f);
			/*if( gState.IsButtonDown(Buttons.LeftShoulder) ) {
				//camera.Move(new Vector3(0, -0.1f, 0));
				camera.Move(new Vector3(0, 0, 0.3f));
			} else if( gState.IsButtonDown(Buttons.RightShoulder) ) {
				//camera.Move(new Vector3(0, 0.1f, 0));
				camera.Move(new Vector3(0, 0, -0.3f));
			}*/
	
			
			// update entities
			foreach( Faction faction in Faction.Factions ) {
				LinkedListNode<FactionEntity> fe = faction.FactionEntities.First;				
				LinkedListNode<FactionEntity> next;
				while( fe != null ) {
					next = fe.Next;
					fe.Value.Update(gameTime);
					fe = next;
				}
				foreach( DataTrain dt in faction.DataTrains ) {
					dt.Update(gameTime);
				}
			}
			
			/*bool hit = false;
			foreach( Entity entity in structures ) {
				entity.ColObject.Body.LinearVelocity = Vector2.Zero;
				if( Vector3.Distance(entity.Position, player.Position) < ((ColCircle)player.ColObject).Radius + ((ColCircle)entity.ColObject).Radius) {
					if( ShowCollisionObjects ) ((ColCircle)entity.ColObject).SetColor(Color.Red);					
					hit = true;					
				} else {
					if( ShowCollisionObjects ) ((ColCircle)entity.ColObject).SetColor(Color.Green);
				}
			}
			if( hit ) {
				if( ShowCollisionObjects ) ((ColCircle)player.ColObject).SetColor(Color.Red);
				GamePad.SetVibration(PlayerIndex.One, player.ColObject.Body.LinearVelocity.X, player.ColObject.Body.LinearVelocity.Y);
			} else {
				if( ShowCollisionObjects ) ((ColCircle)player.ColObject).SetColor(Color.Green);
				GamePad.SetVibration(PlayerIndex.One, 0, 0);
			}*/

			// hittest bullets
			foreach( Faction faction in Faction.Factions ) {
				Bullets.HitTest(faction.FactionEntities);
			}
			Bullets.Update(gameTime);

			Faction.Update(gameTime);

			Vector3 avgPosition = playerAvgPosition();
			Listener.Position = avgPosition;
			camera.Move(avgPosition - lastPlayerPos + new Vector3(0, 0, camMove));
			//camera.Move(-defaultCamPos * cameraZoom);
			//cameraZoom -= gState.ThumbSticks.Right.Y / 50.0f;
			//camera.Move(defaultCamPos * cameraZoom);
			lastPlayerPos = avgPosition;
			

			// fps
			elapsedTime += gameTime.ElapsedGameTime;
			if( elapsedTime > TimeSpan.FromSeconds(1) ) {
				elapsedTime -= TimeSpan.FromSeconds(1);
				frameRate = frameCounter;
				frameCounter = 0;
			}
		}

		internal override void Draw(RealGameTime gameTime) {
			frameCounter++;
            ShaderSystem.AnimateTexture();

			foreach( KeyValuePair<PlayerIndex, Player> pair in Players ) {
				((PlayerEntity)pair.Value.Entity).UpdateLifeDraw();
			}

            graphics.GraphicsDevice.Clear(Color.Black);


            //worldParameter.SetValue(Matrix.Identity);
            ShaderSystem.ViewParameter.SetValue(camera.View);
            ShaderSystem.ProjectionParameter.SetValue(camera.Projection);
            ShaderSystem.CameraPositionParameter.SetValue(camera.Position);

            ShaderSystem.SetParametersFor(ShaderSystem.ParametersPresets.Game);
   
            //Background.Draw(gameTime);

            // draw subsurface
			CurSubSurface.Draw(gameTime, camera); 
			NextSubSurface.Draw(gameTime, camera);
		
			// draw datatrains
            /*
			foreach( Faction faction in Faction.Factions ) {
				foreach( FactionEntity fe in faction.DataTrains ) {
					Matrix[] transforms = new Matrix[fe.Entity.Model.Bones.Count];
					fe.Entity.Model.CopyAbsoluteBoneTransformsTo(transforms);
					foreach( ModelMesh mesh in fe.Entity.Model.Meshes ) {
						foreach( BasicEffect meshEffect in mesh.Effects ) {
							meshEffect.PreferPerPixelLighting = true;
							meshEffect.AmbientLightColor = new Vector3(0.7f, 0.6f, 0.2f);
							if( fe.Faction != Faction.None ) {
								meshEffect.DiffuseColor = fe.Faction.Color.ToVector3();
							} else {
								meshEffect.DiffuseColor = Vector3.Zero;
							}
							meshEffect.EnableDefaultLighting();
							meshEffect.Projection = camera.Projection;
							meshEffect.View = camera.View;

							meshEffect.FogColor = Color.Black.ToVector3();
							meshEffect.FogEnabled = true;
							meshEffect.FogStart = 80.0f;
							meshEffect.FogEnd = 120.0f;
							meshEffect.EnableDefaultLighting();

							meshEffect.World = transforms[mesh.ParentBone.Index] * fe.Entity.Scale * fe.Entity.RotationMatrix * fe.Entity.PositionMatrix;
						}
						mesh.Draw();
					}
					if( ShowCollisionObjects ) {
						fe.Entity.DrawColObject(camera);
					}
				}
			}	
            */


			// draw surface
            CurMap.Draw(camera);
			NextMap.Draw(camera);

			// draw factions
			foreach( Faction faction in Faction.Factions ) {
				foreach( FactionEntity fe in faction.FactionEntities ) {
					foreach( ModelMesh mesh in fe.Entity.Model.Meshes ) {
                        if (fe.Faction != Faction.None)
                        {
                            ShaderSystem.ColorParameter.SetValue(fe.Faction.Color.ToVector4());
                        }
                        else
                        {
                            ShaderSystem.ColorParameter.SetValue(Vector4.Zero);
                        }
                        ShaderSystem.WorldParameter.SetValue(fe.Entity.Transforms[mesh.ParentBone.Index] * fe.Entity.Scale * fe.Entity.RotationMatrix * fe.Entity.PositionMatrix);
                        ShaderSystem.Draw(mesh);
					}
					if( ShowCollisionObjects ) {						
						fe.Entity.DrawColObject(camera);
					}
				}				
			}

			// draw bullets
			Bullets.Draw(camera);

			List<PlayerEntity> sortedPlayers = new List<PlayerEntity>();
			foreach( KeyValuePair<PlayerIndex, Player> pair in RacerGame.Players ) {
				sortedPlayers.Add((PlayerEntity)pair.Value.Entity);				
			}
			sortedPlayers.Sort(new Comparison<PlayerEntity>(delegate(PlayerEntity e1, PlayerEntity e2){				
				if( e1.Position.Z < e2.Position.Z ) return 1;
				if( e1.Position.Z == e2.Position.Z ) return 0;
				return -1;
			}));
			foreach( PlayerEntity pe in sortedPlayers ) {
				pe.LifeQuad.Draw(camera);
			}
									
			// info
			//string text = "Red: " + Faction.Red.Credits + " Green: " + Faction.Green.Credits + " Blue: " + Faction.Blue.Credits + " Yellow: " + Faction.Yellow.Credits;
			//string text = frameRate + "fps, Life: " + player.Entity.Life.ToString("N0") + ", Simulator: " + Simulator.GeomList.Count + " " + Simulator.BodyList.Count + " - " + (float)Math.Pow(player.Entity.ColObject.LinearVelocity.Length(),0.3);
			int numEntities = 0;
			foreach( Faction f in Faction.Factions ) {
				numEntities += f.FactionEntities.Count;
			}
			/*string text = frameRate + "fps, Entities: " + numEntities + ", Level: " + level;
			
			spriteBatch.DrawString(SpriteFont, text, new Vector2(33, 13), Color.Black);
			spriteBatch.DrawString(SpriteFont, text, new Vector2(32, 12), Color.White);*/

			spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.SaveState);

			if( showScore && Players.Count > 1 ) {
				string score = "";
				int playerCount = 0;
				foreach( KeyValuePair<PlayerIndex, Player> pair in RacerGame.Players ) {
					score += pair.Value.Score;
					playerCount++;
					if( playerCount < RacerGame.Players.Count ) {
						score += ":";
					}
				}
				Vector2 FontOrigin = SpriteFont.MeasureString(score) / 2;
				spriteBatch.DrawString(SpriteFont, score, new Vector2(device.Viewport.Width / 2, device.Viewport.Height / 2), Color.White, 0, FontOrigin, 10, SpriteEffects.None, 1);				
			}

			spriteBatch.End();
		}
	}
}